home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Linux / Kubuntu 8.10 / kubuntu-8.10-desktop-i386.iso / casper / filesystem.squashfs / usr / lib / ruby / 1.8 / tempfile.rb < prev    next >
Text File  |  2008-04-21  |  5KB  |  210 lines

  1. #
  2. # tempfile - manipulates temporary files
  3. #
  4. # $Id: tempfile.rb 16127 2008-04-21 09:43:44Z knu $
  5. #
  6.  
  7. require 'delegate'
  8. require 'tmpdir'
  9.  
  10. # A class for managing temporary files.  This library is written to be
  11. # thread safe.
  12. class Tempfile < DelegateClass(File)
  13.   MAX_TRY = 10
  14.   @@cleanlist = []
  15.  
  16.   # Creates a temporary file of mode 0600 in the temporary directory,
  17.   # opens it with mode "w+", and returns a Tempfile object which
  18.   # represents the created temporary file.  A Tempfile object can be
  19.   # treated just like a normal File object.
  20.   #
  21.   # The basename parameter is used to determine the name of a
  22.   # temporary file.  If an Array is given, the first element is used
  23.   # as prefix string and the second as suffix string, respectively.
  24.   # Otherwise it is treated as prefix string.
  25.   #
  26.   # If tmpdir is omitted, the temporary directory is determined by
  27.   # Dir::tmpdir provided by 'tmpdir.rb'.
  28.   # When $SAFE > 0 and the given tmpdir is tainted, it uses
  29.   # /tmp. (Note that ENV values are tainted by default)
  30.   def initialize(basename, tmpdir=Dir::tmpdir)
  31.     if $SAFE > 0 and tmpdir.tainted?
  32.       tmpdir = '/tmp'
  33.     end
  34.  
  35.     lock = nil
  36.     n = failure = 0
  37.     
  38.     begin
  39.       Thread.critical = true
  40.  
  41.       begin
  42.     tmpname = File.join(tmpdir, make_tmpname(basename, n))
  43.     lock = tmpname + '.lock'
  44.     n += 1
  45.       end while @@cleanlist.include?(tmpname) or
  46.     File.exist?(lock) or File.exist?(tmpname)
  47.  
  48.       Dir.mkdir(lock)
  49.     rescue
  50.       failure += 1
  51.       retry if failure < MAX_TRY
  52.       raise "cannot generate tempfile `%s'" % tmpname
  53.     ensure
  54.       Thread.critical = false
  55.     end
  56.  
  57.     @data = [tmpname]
  58.     @clean_proc = Tempfile.callback(@data)
  59.     ObjectSpace.define_finalizer(self, @clean_proc)
  60.  
  61.     @tmpfile = File.open(tmpname, File::RDWR|File::CREAT|File::EXCL, 0600)
  62.     @tmpname = tmpname
  63.     @@cleanlist << @tmpname
  64.     @data[1] = @tmpfile
  65.     @data[2] = @@cleanlist
  66.  
  67.     super(@tmpfile)
  68.  
  69.     # Now we have all the File/IO methods defined, you must not
  70.     # carelessly put bare puts(), etc. after this.
  71.  
  72.     Dir.rmdir(lock)
  73.   end
  74.  
  75.   def make_tmpname(basename, n)
  76.     case basename
  77.     when Array
  78.       prefix, suffix = *basename
  79.     else
  80.       prefix, suffix = basename, ''
  81.     end
  82.  
  83.     t = Time.now.strftime("%Y%m%d")
  84.     path = "#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}-#{n}#{suffix}"
  85.   end
  86.   private :make_tmpname
  87.  
  88.   # Opens or reopens the file with mode "r+".
  89.   def open
  90.     @tmpfile.close if @tmpfile
  91.     @tmpfile = File.open(@tmpname, 'r+')
  92.     @data[1] = @tmpfile
  93.     __setobj__(@tmpfile)
  94.   end
  95.  
  96.   def _close    # :nodoc:
  97.     @tmpfile.close if @tmpfile
  98.     @tmpfile = nil
  99.     @data[1] = nil if @data
  100.   end    
  101.   protected :_close
  102.  
  103.   # Closes the file.  If the optional flag is true, unlinks the file
  104.   # after closing.
  105.   #
  106.   # If you don't explicitly unlink the temporary file, the removal
  107.   # will be delayed until the object is finalized.
  108.   def close(unlink_now=false)
  109.     if unlink_now
  110.       close!
  111.     else
  112.       _close
  113.     end
  114.   end
  115.  
  116.   # Closes and unlinks the file.
  117.   def close!
  118.     _close
  119.     @clean_proc.call
  120.     ObjectSpace.undefine_finalizer(self)
  121.     @data = @tmpname = nil
  122.   end
  123.  
  124.   # Unlinks the file.  On UNIX-like systems, it is often a good idea
  125.   # to unlink a temporary file immediately after creating and opening
  126.   # it, because it leaves other programs zero chance to access the
  127.   # file.
  128.   def unlink
  129.     # keep this order for thread safeness
  130.     begin
  131.       File.unlink(@tmpname) if File.exist?(@tmpname)
  132.       @@cleanlist.delete(@tmpname)
  133.       @data = @tmpname = nil
  134.       ObjectSpace.undefine_finalizer(self)
  135.     rescue Errno::EACCES
  136.       # may not be able to unlink on Windows; just ignore
  137.     end
  138.   end
  139.   alias delete unlink
  140.  
  141.   # Returns the full path name of the temporary file.
  142.   def path
  143.     @tmpname
  144.   end
  145.  
  146.   # Returns the size of the temporary file.  As a side effect, the IO
  147.   # buffer is flushed before determining the size.
  148.   def size
  149.     if @tmpfile
  150.       @tmpfile.flush
  151.       @tmpfile.stat.size
  152.     else
  153.       0
  154.     end
  155.   end
  156.   alias length size
  157.  
  158.   class << self
  159.     def callback(data)    # :nodoc:
  160.       pid = $$
  161.       lambda{
  162.     if pid == $$ 
  163.       path, tmpfile, cleanlist = *data
  164.  
  165.       print "removing ", path, "..." if $DEBUG
  166.  
  167.       tmpfile.close if tmpfile
  168.  
  169.       # keep this order for thread safeness
  170.       File.unlink(path) if File.exist?(path)
  171.       cleanlist.delete(path) if cleanlist
  172.  
  173.       print "done\n" if $DEBUG
  174.     end
  175.       }
  176.     end
  177.  
  178.     # If no block is given, this is a synonym for new().
  179.     #
  180.     # If a block is given, it will be passed tempfile as an argument,
  181.     # and the tempfile will automatically be closed when the block
  182.     # terminates.  In this case, open() returns nil.
  183.     def open(*args)
  184.       tempfile = new(*args)
  185.  
  186.       if block_given?
  187.     begin
  188.       yield(tempfile)
  189.     ensure
  190.       tempfile.close
  191.     end
  192.  
  193.     nil
  194.       else
  195.     tempfile
  196.       end
  197.     end
  198.   end
  199. end
  200.  
  201. if __FILE__ == $0
  202. #  $DEBUG = true
  203.   f = Tempfile.new("foo")
  204.   f.print("foo\n")
  205.   f.close
  206.   f.open
  207.   p f.gets # => "foo\n"
  208.   f.close!
  209. end
  210.